/**
 * Board -
 * 
 * @author 
 * @version 2010.10.24
 */

class Board extends Object;

var TwoDArray   board;
var TwoDArray   blob;
var Piece       activePiece;
var Piece       nextPiece;

var int BOARD_HEIGHT;
var int BOARD_WIDTH;
var int PIECE_SIZE;
var int		     score;

	/*static final int[] probs = {L.PROBABILITY, Pipe.PROBABILITY,
											ReverseL.PROBABILITY, ReverseSquigly.PROBABILITY,
											Square.PROBABILITY, Squigly.PROBABILITY,
											Tee.PROBABILITY};

	*/

/**
 * setUp - replacement constructor. must be called explicitly
 * after creating a board.
 * @author
 * @version 2010.10.24
 */
public function setUp() {
	local int i, j;
	score = 0;
	BOARD_HEIGHT = 10;
	BOARD_WIDTH  = 8;
	PIECE_SIZE   = 4;

	board = new class'TwoDArray';
	blob  = new class'TwoDArray';
	
	blob.setDimensions(BOARD_HEIGHT, BOARD_WIDTH);
	board.setDimensions(BOARD_HEIGHT, BOARD_WIDTH);

	//Zero all elements
	for(i = 0; i < BOARD_HEIGHT; i++) {
		for(j = 0; j < BOARD_WIDTH; j++) {
			blob.set(i, j, 0);
			board.set(i, j, 0);
		}
	}

	activePiece = new class'Piece'; 
	nextPiece   = new class'Piece';
	
	activePiece = getRandomPiece();
	nextPiece   = getRandomPiece();
}

/**
 * getRandomPiece - currently just returns a square
 * 
 * @todo: implement randomized piece generation (unreal has a Rand() function)
 * @author
 * @version 2010.10.24
 */
private function Piece getRandomPiece() {
	local Square sq;
	local Pipe p;
	local L l;
	local ReverseL rl;
	local ReverseSquigly rs;
	local Squigly s;
	local Tee t;
	
	local int random;

	random = Rand(7);

	switch( random )
	{
		case 0:
			p = new class'Pipe';
			p.setUp();
			return p;
		case 1:
			l = new class'L';
			l.setUp();
			return l;
		case 2:
			rl = new class'ReverseL';
			rl.setUp();
			return rl;
		case 3:
			rs = new class'ReverseSquigly';
			rs.setUp();
			return rs;
		case 4:
			s = new class'Squigly';
			s.setUp();
			return s;
		case 5:
			t = new class'Tee';
			t.setUp();
			return t;
		default:
			sq = new class'Square';
			sq.setUp();
			return sq;
	}
	
}

/**
 * rotateActivePiece - 
 * 
 * @author 
 * @version 2010.10.24
 */
public function rotateActivePiece() {
	activePiece.changeState();

	if(testRotateCollision())
		activePiece.restoreState();
	else
		translatePiecesToBoard();
}

/**
 * swapActiveAndNextPiece - sets active piece to next
 * piece and generates new next piece. also runs a check for
 * full rows.
 * 
 * @author
 * @version 2010.10.24
 */
function swapActiveAndNextPiece() {
	activePieceToBlob();
	checkForFullRows();

	activePiece = nextPiece;
	nextPiece   = getRandomPiece();

	if(gameEnded()) {
		// Game.getInstance().endGame();
	}
}
     
/**
 * activePieceToBlob - translates the current piece to the blob.
 * 
 * @author
 * @version 2010.10.24
 */
function activePieceToBlob() {
	local TwoDArray piece;
	local int       cols, rows, i, j;

	cols  = activePiece.getPosition().X;
	rows  = activePiece.getPosition().Y;
	piece = activePiece.getCurrentBlockArray();

	for(i = 0; i < PIECE_SIZE; i++) {
		for(j = 0; j < PIECE_SIZE; j++) {
			//don't want to cover up previous pieces with 0 (i.e. white)
			if(piece.get(i, j) != 0) {
				   blob.set(rows+i, cols+j, piece.get(i,j));
			}
		}
	}
}

/**
 * translatePiecesToBoard - 
 * 
 * @author
 * @version 2010.10.23
 */
function translatePiecesToBoard() {
	local TwoDArray piece;
	local int       i, j, cols, rows;

	piece = activePiece.getCurrentBlockArray();
    	 
	//First, copy the "blob's" blocks to our board
	for(i = 0; i < BOARD_HEIGHT; i++)
		for(j = 0; j < BOARD_WIDTH; j++)
			board.set(i, j, blob.get(i,j));
    	 
	//Second, copy the "activePiece" blocks to the 
	//appropriate place on the board using the coordinates
	//of the activePiece
	cols = activePiece.getPosition().x;
	rows = activePiece.getPosition().y;
    	 
	for(i = 0; i < PIECE_SIZE; i++) {
		for(j = 0; j < PIECE_SIZE; j++) {
			//if you add all the blocks, then you get an out-of-bounds when not needed and you cover up other pieces with WHITE
			if(piece.get(i,j) != 0)
				board.set(rows+i, cols+j, piece.get(i,j));	 
		}
	}
}

/**
 * movePiece - use negative for left, positive for right
 * 
 * @author
 * @version 2010.10.24
 */
function movePiece(int distance) {
	local int x, y;

	x = activePiece.getPosition().x;
	y = activePiece.getPosition().y;

	activePiece.setPosition(x + distance, y);

	if(testLeftRightCollision() || testDropCollision())
		activePiece.setPosition(x, y);
	else
		translatePiecesToBoard();
}

/**
 * instantDrop - drops the active piece as far down as possible
 * 
 * @author
 * @version 2010.10.24
 */
function instantDrop() {
	local int x;

	x = activePiece.getPosition().x;

	while(!testDropCollision())
		activePiece.setPosition(x, activePiece.getPosition().y + 1);

	//since it goes until it collides with something, have to subtract 1
	activePiece.setPosition(x, activePiece.getPosition().y - 1);
	translatePiecesToBoard();
}
     
/**
 * dropActivePiece - moves active piece down by one.
 * 
 * @author 
 * @version 2010.10.24
 */
function dropActivePiece() {
	local int col, row;
	
	col = activePiece.getPosition().X;
    row = activePiece.getPosition().Y;

	activePiece.setPosition(col, row+1);

	//Debugging
	`log("ROW: "$row$"    COL: "$col);

	if(testDropCollision()) {
		activePiece.setPosition(col, row);
		swapActiveAndNextPiece();
	}
    	 
	translatePiecesToBoard();
}
     
/**
 * rowIsEmpty - Determines whether a given row is void of blocks.
 * 
 * @author Clinton Freeman
 * @version 2010.09.11
 */
function bool rowIsEmpty(int row) {
	local int i;

	for(i = 0; i < BOARD_WIDTH; i++) {
		//If just one block is full, return false
		if(blob.get(row,i) != 0)
			return false;
	}

	//All blocks were empty
	return true;
}
     
/**
 * rowIsFull - Determines whether a given row is filled with blocks.
 * 
 * @author Clinton Freeman
 * @version 2010.09.11
 */
function bool rowIsFull(int row) {
	local int i;

	for(i = 0; i < BOARD_WIDTH; i++) {
		//If just one block is empty, return false
		if(blob.get(row,i) == 0)
			return false;
	}
    	 
	//All blocks were filled
	return true;   	 
}
     
/**
 * clearRow - Called after we've determined that a row is full.
 * Should move each row that is above it down by one.
 * 
 * @author Clinton Freeman
 * @version 2010.09.11
 */
function clearRow(int fullRow) {
	
	local int firstEmptyRow, i, j;

	score = score + 100;
	firstEmptyRow = 0;

	//Begin by finding the first empty row above the filled row.
	for(i = (fullRow - 1); i > 0; i--) {
		if(rowIsEmpty(i)) {
			firstEmptyRow = i;
			break;
		}
	}
		 
	//For each row in between the row that is full and the first
	//row that is empty, move it down by one.
	for(i = (fullRow - 1); i > firstEmptyRow; i--) {
		for(j = 0; j < BOARD_WIDTH; j++) {
			blob.set(i+1,j,blob.get(i,j));
		} 
	}
}

/**
 * checkForFullRows - Iterates through the blob to determine if a row 
 * has been filled with blocks, and shifts rows above it down should
 * it find one.
 * 
 * @author Clinton Freeman
 * @version 2010.09.11
 */
function checkForFullRows() {
	local int i;

	for (i = 0; i < BOARD_HEIGHT; i++) {
		if(rowIsFull(i)) {
			clearRow(i);
			//Game.getInstance().score();
		}
	}
}

/**
 * gameEnded - 
 * 
 * @author
 * @version 2010.10.24
 */
function bool gameEnded() {
	return (testDropCollision()) ? true : false;
}

/**
 * testDropCollision - 
 * 
 * @author
 * @version 2010.10.24
 */
function bool testDropCollision() {
	local TwoDArray currentState;
	local int       starty, stopy, startx, stopx, x, y;

	currentState = activePiece.getCurrentBlockArray();
	starty       = activePiece.getPosition().y;

	if(testBottomCollision())
		return true;

	stopy  = starty + PIECE_SIZE;
	startx = activePiece.getPosition().x;

	for(y = 0; y < PIECE_SIZE; y++) {
		for(x = 0; x < PIECE_SIZE; x++) {
			//What will currentState be an array of?
			if(currentState.get(y,x) != 0) {
				if(blob.get(starty + y,startx + x) != 0) {
                         return true;
				}
			}
		}
	}

	return false;
}

/**
 * testBottomCollision - 
 * 
 * @todo magic numbers
 * @author
 * @version 2010.10.24
 */
function bool testBottomCollision() {
	local TwoDArray currentState;
	local int       starty, bottom, x, y;
	local bool      done;

	currentState = activePiece.getCurrentBlockArray();
	starty       = activePiece.getPosition().y;
	done         = false;

	for(bottom = 3; bottom >= 0; bottom--) {
		for(x = 0; x < 4; x++) {
			if(currentState.get(bottom,x) != 0) {
				done = true;
				break;
			}
		}

		if(done)
			break;
	}

	return (bottom + starty > (BOARD_HEIGHT - 1)) ? true : false;
}

/**
 * testLeftRightCollision - 
 * 
 * @author
 * @version 2010.10.24
 */
function bool testLeftRightCollision() {
	local int       piecex, left, right, y, x;
	local TwoDArray currentState; 

	piecex       = activePiece.getPosition().x;
	currentState = activePiece.getCurrentBlockArray();

	left  = PIECE_SIZE;
	right = 0;

	for(y = 0; y < PIECE_SIZE; y++) {
		for(x = 0; x < PIECE_SIZE; x++) {
			if(currentState.get(y,x) != 0) {
				if(x < left)
					left = x;
				if(x > right)
					right = x;
			}
		}
	}

	return ((left + piecex) < 0 || (right + piecex) > BOARD_WIDTH - 1) ? true : false;
}

/**
 * testRotateCollision - 
 * 
 * @author
 * @version 2010.10.24
 */
function bool testRotateCollision() {
	return (testLeftRightCollision() || testDropCollision()) ? true : false;
}

//Getters
function int       getWidth()     { return BOARD_WIDTH;  }
function int       getHeight()    { return BOARD_HEIGHT; }
function TwoDArray getBoard()     { return board;        }
function Piece     getNextPiece() { return nextPiece;    }


